跳到主要内容

配置管理

最佳实践思想

  1. 区分必选值与可选择
  2. 配置文件与选项解耦
  3. 避免复杂
  4. 简单化, 除了必选的值, 默认值应该是科学的, 合理的, 最佳实践的值
  5. 防御编程, 防止其他人乱改和运行时修改配置, 如果用户手一抖, 一秒钟填写成1分钟, 对不合理的配置直接panic
  6. 配置版本和应用对齐, 回滚应用时, 应用和配置文件应该一一对应, 一起回滚, 在配置中心或者k8s配置

最佳实践

proto + 配置语言(json/yaml/toml)

proto可以进行高亮 yaml编写配置就可以与proto进行解耦

演进

函数配置

防止在运行时修改DailOption配置 优点:

  1. 告诉你可选和必选值
  2. 函数内方便使用默认值 扩展: 如果要扩展, 必须新增函数, 例如DailDatabase等才可以扩展
type DailOption struct {
// dailOption, 小写, 意味着没有任何人有修改里面的值
f func(*dailOption)
}

func Dial(network, address string, options ...DailOption) (Conn, error){
// 默认值
do := dailOption {
dial: net.Dial,
}
// 如果没有传递options, 也不会造成影响
for _, option := range options {
option.f(&do)
}
}

或者:

type DailOption func(*dailOption)

func Dial(network, address string, options ...DailOption) (Conn, error){
do := dailOption {
dial: net.Dial,
}
for _, option := range options {
option(&do)
}
}

函数配置, 包含返回值

用于单元测试等, 需要切换参数配置时 改之前与改之后

type option func(f *Foo) option
func Verbosity(v int) option {
return func(f *Foo) option {
prev := f.verbostiy
f.verbostiy = v
return Verbosity(prev)
}
}

func DoSomethingVerbosity(foo *Foo, verbosity int) {
prev := foo.Option(pkg.Verbosity(verbostiy))
defer foo.Option(prev)
}

包含interface的函数配置

type GreeterClient interface {
SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*SayHelloReply, error)
}

type CallOption interface {
before(*callInfo) error
after(*callInfo)
}
// EmptyCallOption 不改变 CallCallOption
type EmptyCallOption struct{}

// 对原有的grpc.CallOption进行扩展
type TimeoutCallOption struct {
grpc.EmptyCallOption
Timeout time.Duration
}

其它配置的缺点

传统结构体(不加指针)

缺点: 不加指针是copy该结构体, 无法使用默认值, 以及0值和设定的值无法区分

func Dail(c Config) (cn Conn, err error)

配置文件解析

如果是把配置文件解析到go结构体, 那么会产生以下问题: 例如Redis, 如果你使用了配置文件映射到结构体, 那么如果别人使用的是redis.NewConn, 与你的配置文件冲突, 那么副作用是什么?

// config.yaml
server:
address: xxx

// struct
type Config struct {
Address string
}

func main() {
c := Config {Address: xxx}
// 正常使用
c := Redis.Config {
Address: c.Address
}
// 其它使用
r, _ := &redis.NewConn(c)
c.Addr = "xxx" // 副作用是什么?
}

参数扩展

这样写不知道有多少个的方法

func Dial(network, address, timeout string)
func DialDatabase(network, address, timeout, database string) (Conn, error)

函数可选参数

缺点: 参数不清晰, 如果填多个Config, 哪个生效呢? 有什么副作用?

func Dail(c ...Config) (cn Conn, err error)

指针默认配置

缺点: 使用nil来表示使用默认的值, 这个nil作为默认值不是特别好. nil不应该作为公共函数的参数

func Dail(c *Config) (cn Conn, err error)

func main() {
Dail(nil)
}